<?php declare(strict_types=1);

namespace Blockgolde\HyperfBusinessWrapper\Exception\Handler;

use Throwable;
use Psr\Log\LoggerInterface;
use Hyperf\Logger\LoggerFactory;
use Psr\Http\Message\ResponseInterface;
use Hyperf\Validation\ValidationException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Database\Exception\QueryException;
use Hyperf\HttpMessage\Exception\MethodNotAllowedHttpException;
use Hyperf\HttpMessage\Exception\NotFoundHttpException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Blockgolde\HyperfBusinessWrapper\Constants\ErrorCode;
use Blockgolde\HyperfBusinessWrapper\Utils\ResponseService;
use Blockgolde\HyperfBusinessWrapper\Exception\ApiException;
use Blockgolde\HyperfBusinessWrapper\Exception\ThrottleRequestsException;

class ApiExceptionHandler extends ExceptionHandler
{

    /**
     * @var \Psr\Log\LoggerInterface
     */
    protected LoggerInterface  $logger;

    public function __construct(LoggerFactory $logger)
    {
        $this->logger = $logger->get('API');
    }

    public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
    {
         // 接口错误
         $this->logger->error(
            sprintf(
                '%s[%s] in %s',
                $throwable->getMessage(),
                $throwable->getLine(),
                $throwable->getFile()
            )
        );
        // 生产环境下 固定提示 服务器内部错误
        $code = 9999;
        $msg = ErrorCode::getMessage(ErrorCode::INTERNAL_ERROR);
        if (env('APP_ENV') !== 'pro') {
            if ($throwable instanceof ApiException) {
                $code = $throwable->getCode() ?: ErrorCode::SERVER_ERROR;
                $msg = $throwable->getMessage();
            } elseif ($throwable instanceof ValidationException) {
                $code = ErrorCode::ERR_HTTP_UNPROCESSABLE_ENTITY;
                $msg = $throwable->validator->errors()->first();
            } elseif ($throwable instanceof QueryException) {
                $code = ErrorCode::ERR_QUERY;
            } elseif ($throwable instanceof \PDOException) {
                $code = ErrorCode::ERR_DB;
            } elseif ($throwable instanceof ModelNotFoundException) {
                $code = ErrorCode::ERR_MODEL;
            } elseif ($throwable instanceof NotFoundHttpException) {
                $code = ErrorCode::ERR_NON_EXISTENT;
                $msg = '路由未定义或不支持当前请求';
            } elseif ($throwable instanceof MethodNotAllowedHttpException) {
                $code = ErrorCode::ERR_DB;
                $msg = $throwable->getMessage();
            } elseif ($throwable instanceof ThrottleRequestsException) {
                $code = ErrorCode::REQUEST_FREQUENTLY;
            } else {
                // 未知错误
                $msg = $throwable->getMessage();
                $this->logger->error($throwable->getTraceAsString()); // 调用栈日志
                $code = $throwable->getCode();
            }
        }
        $this->stopPropagation();
        return $response->withHeader(
            'Content-Type',
            'application/json'
        )->withStatus(ErrorCode::INTERNAL_ERROR)->withBody(
            new SwooleStream(ResponseService::responseJson((int)$code, $msg))
        );
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}
